package com.iisigroup.adrclient.service.impl;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.swing.JTextArea;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.iisigroup.adrclient.common.DateUtils;
import com.iisigroup.adrclient.common.GenericService;
import com.iisigroup.adrclient.common.StringUtils;
import com.iisigroup.adrclient.dao.AdrClientDao;
import com.iisigroup.adrclient.service.AdrClientService;
import com.iisigroup.adrclient.vo.AdrStrBlockOrderVO;
import com.iisigroup.adrclient.vo.AdrStrBlockVO;
import com.iisigroup.adrclient.vo.ResultVO;

@Service
@Transactional
public class AdrClientServiceImpl extends GenericService implements AdrClientService {
	
	private static final String COMPARE_PATH = new File("").getAbsolutePath().toString() + "/compare/";
	private static final String NEW_FILE = COMPARE_PATH + "%s/%s.DIFF.NEW.L%d.txt";
	private static final String OLD_FILE = COMPARE_PATH + "%s/%s.DIFF.OLD.L%d.txt";
	private static final String RESULT_FILE = COMPARE_PATH + "%s/%s.DIFF.RESULT.L%d.txt";
	private static final Integer L150 = 150;
	private static final Integer L168 = 168;
	private static final Integer L260 = 260;
	private static final Integer L270 = 270;
	private static final Integer POST = 84;
	private static final Integer AREA = 31;
	private static final Integer SPRD = 70;
	private static final Integer BUFFER_SIZE = 32768;
	private static final String ENCODING = "MS950";
	private static final String TEMPLATE = "比較%s 相同: %d 新增: %d 減少: %d";
	private Map<Integer, String> itemMap = new HashMap<Integer, String>();
	@Autowired
	private AdrClientDao dao;
	
	public AdrClientServiceImpl() {
		itemMap.put(L150, "鄰街廓");
		itemMap.put(L168, "村里街廓");
		itemMap.put(L260, "地址明細");
		itemMap.put(L270, "戶籍明細");
		itemMap.put(POST, "POST");
		itemMap.put(AREA, "村里代碼");
		itemMap.put(SPRD, "特殊路街");
	}
	
	@Override
	public Boolean uploadAdr200(String year, String countyCode, String url, JTextArea console) {
		return dao.uploadAdr200(year, countyCode, url, console);
	}
	@Override
	public Integer countAdr200(String year, String countyCode) {
		return dao.countAdr200(year, countyCode);
	}
	@Override
	public Boolean uploadHud250(String year, String countyCode, String url, JTextArea console) {
		return dao.uploadHud250(year, countyCode, url, console);
	}
	@Override
	public Integer countHud250(String year, String countyCode) {
		return dao.countHud250(year, countyCode);
	}
	@Override
	public List<ResultVO> getAdr220(String year, String countyCode) {
		return dao.getAdr220(year, countyCode);
	}
	@Override
	public Boolean outputAdr220(String year, String countyCode, JTextArea console) {
		return dao.outputAdr220(year, countyCode, console);
	}
	@Override
	public Integer countAdr220(String year, String countyCode) {
		return dao.countAdr220(year, countyCode);
	}
	@Override
	public List<ResultVO> getAdr260(String year, String countyCode) {
		return dao.getAdr260(year, countyCode);
	}
	@Override
	public Boolean outputAdr260(String year, String countyCode, JTextArea console) {
		return dao.outputAdr260(year, countyCode, console);
	}
	@Override
	public Integer countAdr260(String year, String countyCode) {
		return dao.countAdr260(year, countyCode);
	}
	@Override
	public List<ResultVO> getAdr270(String year, String countyCode) {
		return dao.getAdr270(year, countyCode);
	}
	@Override
	public Boolean outputAdr270(String year, String countyCode, JTextArea console) {
		return dao.outputAdr270(year, countyCode, console);
	}
	@Override
	public Integer countAdr270(String year, String countyCode) {
		return dao.countAdr270(year, countyCode);
	}
	@Override
	public List<ResultVO> getAdrPost(String year, String countyCode) {
		return dao.getAdrPost(year, countyCode);
	}
	@Override
	public Integer countAdrPost(String year, String countyCode) {
		return dao.countAdrPost(year, countyCode);
	}
	@Override
	public Boolean outputAdrPost(String year, String countyCode, JTextArea console) {
		return dao.outputAdrPost(year, countyCode, console);
	}
	@Override
	public List<ResultVO> getAdrSprd(String year) {
		return dao.getAdrSprd(year);
	}
	@Override
	public Integer countAdrSprd(String year) {
		return dao.countAdrSprd(year);
	}
	@Override
	public Boolean outputAdrSprd(String year, JTextArea console) {
		return dao.outputAdrSprd(year, console);
	}
	@Override
	public Boolean outputArea(String year, JTextArea console) {
		return dao.outputArea(year, console);
	}
	@Override
	public Boolean outputCity(String year, JTextArea console) {
		return dao.outputCity(year, console);
	}
	@Override
	public Map<String, String> getCountyMap() {
		return dao.getCountyMap();
	}
	@Override
	public String compare(String sourceUrl, String targetUrl, Integer length) {
		String result = null;
		final String item = itemMap.get(length);
		final String currentDate = DateUtils.getCurrentDate();
		final File sourceFile = new File(sourceUrl);
		final File targetFile = new File(targetUrl);
		final File sourceDiff = new File(String.format(NEW_FILE, item, currentDate, length));
		final File targetDiff = new File(String.format(OLD_FILE, item, currentDate, length));
		final File resultFile = new File(String.format(RESULT_FILE, item, currentDate, length));
		
		sourceDiff.getParentFile().mkdirs();
		targetDiff.getParentFile().mkdirs();
		resultFile.getParentFile().mkdirs();
		
		BufferedReader sourceReader = null;
		BufferedReader targetReader = null;
		
		BufferedWriter sourceWriter = null;
		BufferedWriter targetWriter = null;
		BufferedWriter resultWriter = null;
		
		Integer same = 0;
		Integer diff = 0;
		
		try {
			sourceWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(sourceDiff), ENCODING), BUFFER_SIZE);
			targetWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(targetDiff), ENCODING), BUFFER_SIZE);
			resultWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(resultFile), ENCODING), BUFFER_SIZE);
			
			Map<String, String> targetMap = new LinkedHashMap<String, String>();
			targetReader = new BufferedReader(new InputStreamReader(new FileInputStream(targetFile), ENCODING), BUFFER_SIZE);
			for (String line = null; (line = targetReader.readLine()) != null;) {
				byte[] bytes = line.getBytes(ENCODING);
				String key = null;
				if (length - L150 == 0) {
					key = StringUtils.removeSpace(new String(Arrays.copyOfRange(bytes, 23, 108), ENCODING));
				}
				if (length - L168 == 0) {
					key = StringUtils.removeSpace(new String(Arrays.copyOfRange(bytes, 23, 119), ENCODING));
				}
				if (length - L260 == 0) {
					key = StringUtils.removeSpace(new String(Arrays.copyOfRange(bytes, 69, 204), ENCODING));
//					key = StringUtils.removeSpace(new String(Arrays.copyOfRange(bytes, 78, 201), ENCODING));
				}
				if (length - L270 == 0) {
					key = StringUtils.removeSpace(new String(Arrays.copyOfRange(bytes, 34, 204), ENCODING));
				}
				if (length - POST == 0 || length - AREA == 0 || length - SPRD == 0) {
					key = StringUtils.removeSpace(line);
				}
				
				targetMap.put(key, line);
			}
			
			sourceReader = new BufferedReader(new InputStreamReader(new FileInputStream(sourceFile), ENCODING), BUFFER_SIZE);
			for (String line = null; (line = sourceReader.readLine()) != null;) {
				byte[] bytes = line.getBytes(ENCODING);
				String key = null;
				if (length - L150 == 0) {
					key = StringUtils.removeSpace(new String(Arrays.copyOfRange(bytes, 23, 108), ENCODING));
				}
				if (length - L168 == 0) {
					key = StringUtils.removeSpace(new String(Arrays.copyOfRange(bytes, 23, 119), ENCODING));
				}
				if (length - L260 == 0) {
					key = StringUtils.removeSpace(new String(Arrays.copyOfRange(bytes, 69, 204), ENCODING));
//					key = StringUtils.removeSpace(new String(Arrays.copyOfRange(bytes, 78, 201), ENCODING));
				}
				if (length - L270 == 0) {
					key = StringUtils.removeSpace(new String(Arrays.copyOfRange(bytes, 34, 204), ENCODING));
				}
				if (length - POST == 0 || length - AREA == 0 || length - SPRD == 0) {
					key = StringUtils.removeSpace(line);
				}
				
				if (targetMap.containsKey(key)) {
					targetMap.remove(key);
					same++;
				} else {
					sourceWriter.write(line);
					sourceWriter.newLine();
					diff++;
				}
			}
			
			Set<Entry<String, String>> set = targetMap.entrySet();
			for (Entry<String, String> entry : set) {
				targetWriter.write(entry.getValue());
				targetWriter.newLine();
			}
			
			result = String.format(TEMPLATE, itemMap.get(length), same, diff, targetMap.size());
			resultWriter.write(result);
		} catch (Exception e) {
			this.logError(e);
		} finally {
			try {
				if (resultWriter != null) {
					resultWriter.close();
				}
				if (targetWriter != null) {
					targetWriter.close();
				}
				if (sourceWriter != null) {
					sourceWriter.close();
				}
				if (sourceReader != null) {
					sourceReader.close();
				}
				if (targetReader != null) {
					targetReader.close();
				}
			} catch (Exception e) {
				this.logError(e);
			}
		}
		
		return result;
	}
	@Override
	public Boolean calAdr380weight(String year, String countyCode) {
		return dao.calAdr380weight(year, countyCode);
	}
	@Override
	public Boolean uploadAdrPost(String year, String countyCode, String url, JTextArea console) {
		return dao.uploadAdrPost(year, countyCode, url, console);
	}
	@Override
	public Boolean uploadAdrPost2(String year, String countyCode, String url, JTextArea console) {
		return dao.uploadAdrPost2(year, countyCode, url, console);
	}
	@Override
	public Boolean uploadAdrArea(String countyCode, String url, JTextArea console) {
		return dao.uploadAdrArea(countyCode, url, console);
	}
	@Override
	public Boolean disableAdr380() {
		return dao.disableAdr380();
	}
	@Override
	public Boolean enableAdr380() {
		return dao.enableAdr380();
	}
	@Override
	public Boolean uploadAdrSprd(String countyCode, String url, JTextArea console) {
		return dao.uploadAdrSprd(countyCode, url, console);
	}
	@Override
	public Boolean deleteAdr380(String year, String countyCode) {
		return dao.deleteAdr380(year, countyCode);
	}
	@Override
	public Boolean initAdr220(String year, String countyCode, JTextArea console) {
		console.append("開始建立工商街廓...\n");
		return dao.initAdr220(year, countyCode);
	}
	@Override
	public Boolean createAdr220(String year, String countyCode, JTextArea console) {
		console.append("開始建立工商街廓...\n");
		return dao.createAdr220(year, countyCode);
	}
	@Override
	public Boolean create150(String year, String countyCode, JTextArea console) {
		console.append("開始建立鄰街廓...\n");
		console.append("正在進行排序...\n");
		List<AdrStrBlockOrderVO> order = this.order150(year, countyCode);
		console.append("正在計算起迄號...\n");
		List<AdrStrBlockVO> list = this.convertToAdrStrBlockVO(order);
		console.append("正在輸入到資料庫...\n");
		return this.insert150(year, countyCode, list, console);
	}
	@Override
	public Boolean create168(String year, String countyCode, JTextArea console) {
		console.append("開始建立村里街廓...\n");
		console.append("正在進行排序...\n");
		List<AdrStrBlockOrderVO> order = this.order168(year, countyCode);
		console.append("正在計算起迄號...\n");
		List<AdrStrBlockVO> list = this.convertToAdrStrBlockVO(order);
		console.append("正在輸入到資料庫...\n");
		return this.insert168(year, countyCode, list, console);
	}
	@Override
	public List<AdrStrBlockVO> convertToAdrStrBlockVO(List<AdrStrBlockOrderVO> list) {
		List<AdrStrBlockVO> result = new ArrayList<AdrStrBlockVO>();
		String bgnNo = null;
		String endNo = null;
		AdrStrBlockOrderVO temp = null;
		
		while (list.size() != 0) {
			AdrStrBlockOrderVO vo = ((LinkedList<AdrStrBlockOrderVO>) list).pop();
			
			if (temp == null) {
				temp = vo;
				bgnNo = vo.getNo1();
				endNo = vo.getNo1();
			}
			
			if (vo.equals(temp)) {
				endNo = vo.getNo1();
			} else {
				if (!vo.getAreaCode().equals(temp.getAreaCode()) || !vo.getStr().equals(temp.getStr())) {
					result.add(this.getAdrStrBlockVO(temp, bgnNo, endNo));
				} else if (vo.getLan() != null && temp.getLan() == null && StringUtils.fullCharNumbersOnly(vo.getLan())) {
					result.add(this.getAdrStrBlockVO(temp, bgnNo, vo.getLan()));
				} else if (vo.getAly() != null && temp.getAly() == null && StringUtils.fullCharNumbersOnly(vo.getAly())) {
					result.add(this.getAdrStrBlockVO(temp, bgnNo, vo.getAly()));
				} else if (vo.getSubAly() != null && temp.getSubAly() == null && StringUtils.fullCharNumbersOnly(vo.getSubAly())) {
					result.add(this.getAdrStrBlockVO(temp, bgnNo, vo.getSubAly()));
				} else {
					result.add(this.getAdrStrBlockVO(temp, bgnNo, endNo));
				}
//				result.add(this.getAdrStrBlockVO(temp, bgnNo, endNo));
				temp = vo;
				bgnNo = vo.getNo1();
				endNo = vo.getNo1();
			}
		}
		
		for (int blockNo = 1; blockNo <= result.size(); blockNo++) {
			result.get(blockNo - 1).setBlockNo(String.valueOf(blockNo));
		}
		
		return result;
	}
	
	private AdrStrBlockVO getAdrStrBlockVO(AdrStrBlockOrderVO vo, String bgnNo, String endNo) {
		AdrStrBlockVO obj = new AdrStrBlockVO();
		obj.setYear(vo.getYear());
		obj.setCountyCode(vo.getCountyCode());
		obj.setAreaCode(vo.getAreaCode());
		obj.setCountyName(vo.getCountyName());
		obj.setTownName(vo.getTownName());
		obj.setVil(vo.getVil());
		obj.setStr(vo.getStr());
		obj.setSec(vo.getSec());
		obj.setSecC(vo.getSecC());
		obj.setLan(vo.getLan());
		obj.setLanC(vo.getLanC());
		obj.setAly(vo.getAly());
		obj.setAlyC(vo.getAlyC());
		obj.setSubAly(vo.getSubAly());
		obj.setSubAlyC(vo.getSubAlyC());
		obj.setBgnNo(bgnNo);
		obj.setEndNo(endNo);
		obj.setOddEven(vo.getOddEven());
		return obj;
	}
	
//	private List<AdrStrBlockOrderVO> readFile380(String url) {
//		Set<AdrStrBlockOrderVO> set = new LinkedHashSet<AdrStrBlockOrderVO>();
//		File file = new File(url);
//		BufferedReader br = null;
//		try {
//			br = new BufferedReader(new InputStreamReader(new FileInputStream(file), ENCODING), BUFFER_SIZE);
//			for (String line = null; (line = br.readLine()) != null; ) {
//				byte[] bytes = line.getBytes(ENCODING);
//				String areaCode = new String(Arrays.copyOfRange(bytes, 358, 368), ENCODING);
//				String countyName = new String(Arrays.copyOfRange(bytes, 113, 119), ENCODING);
//				String townName = new String(Arrays.copyOfRange(bytes, 119, 127), ENCODING);
//				String vil = new String(Arrays.copyOfRange(bytes, 127, 135), ENCODING);
//				String str = new String(Arrays.copyOfRange(bytes, 135, 153), ENCODING);
//				String sec = new String(Arrays.copyOfRange(bytes, 153, 165), ENCODING);
//				String secC = new String(Arrays.copyOfRange(bytes, 165, 167), ENCODING);
//				String lan = new String(Arrays.copyOfRange(bytes, 358, 368), ENCODING);
//				String lanC = new String(Arrays.copyOfRange(bytes, 358, 368), ENCODING);
//				String aly = new String(Arrays.copyOfRange(bytes, 358, 368), ENCODING);
//				String alyC = new String(Arrays.copyOfRange(bytes, 358, 368), ENCODING);
//				String subAly = new String(Arrays.copyOfRange(bytes, 358, 368), ENCODING);
//				String subAlyC = new String(Arrays.copyOfRange(bytes, 358, 368), ENCODING);
//				String no1 = new String(Arrays.copyOfRange(bytes, 358, 368), ENCODING);
//			}
//		} catch (Exception e) {
//			this.logError(e);
//		} 
//		return null;
//	}
	@Override
	public Boolean outputAdr380(String year, String countyCode,
			JTextArea console) {
		return dao.outputAdr380(year, countyCode, console);
	}

	@Override
	public List<AdrStrBlockOrderVO> order150(String year, String countyCode) {
		return dao.order150(year, countyCode);
	}

	@Override
	public List<AdrStrBlockOrderVO> order168(String year, String countyCode) {
		return dao.order168(year, countyCode);
	}

	@Override
	public Boolean insert150(String year, String countyCode,
			List<AdrStrBlockVO> list, JTextArea console) {
		return dao.insert150(year, countyCode, list, console);
	}

	@Override
	public Boolean insert168(String year, String countyCode,
			List<AdrStrBlockVO> list, JTextArea console) {
		return dao.insert168(year, countyCode, list, console);
	}

	@Override
	public Boolean outputAdr150(String year, String countyCode,
			JTextArea console) {
		return dao.outputAdr150(year, countyCode, console);
	}

	@Override
	public Boolean outputAdr168(String year, String countyCode,
			JTextArea console) {
		return dao.outputAdr168(year, countyCode, console);
	}

	@Override
	public Boolean outputWordMap(String year, JTextArea console) {
		return dao.outputWordMap(year, console);
	}

	@Override
	public Boolean uploadWordMap(String url, JTextArea console) {
		return dao.uploadWordMap(url, console);
	}
	
}
